package custom.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.LinkedList;
import java.util.ListIterator;

public class MostRecentlyUsed {

    private LinkedList<String> recentlyUsedList = null;
    private File propertiesFile = null;
    private Properties properties;
    private String sectionName = null;
    private int maxSize = 0;
    private int defaultSize = 0;

    /**
     * This is the default constructor
     * 
     */
    public MostRecentlyUsed(final int defaultSize) {
        this.defaultSize = defaultSize;
        recentlyUsedList = new LinkedList<String>();
        properties = new Properties();
    }

    /**
     * This constructor initializes maxSize
     * 
     */
    public MostRecentlyUsed(final int defaultSize, final int maxSize) throws MRUException {
        this(defaultSize);
        this.setMaxSize(maxSize);
    }

    /**
     * This constructor initializes propertiesFile and sectionName
     * 
     */
    public MostRecentlyUsed(final File propertiesFile, final String sectionName, final int defaultSize) throws MRUException {
        this(defaultSize);
        this.setPropertiesFile(propertiesFile);
        this.setSectionName(sectionName);
        this.getMaxSizeFromProperties();
    }

    /**
     * This constructor initializes propertiesFile, sectionName, and maxSize
     * 
     */
    public MostRecentlyUsed(final File propertiesFile, final String sectionName, final int defaultSize, final int maxSize)
            throws MRUException {
        this(defaultSize);
        this.setPropertiesFile(propertiesFile);
        this.setSectionName(sectionName);
        this.setMaxSize(maxSize);
    }

    /**
     * This method adds the passed in String to recentlyUsedList
     * 
     * @return void
     * 
     */
    public void add(final String item) {
        // Make sure string we are adding is unique
        boolean notInList = true;
        ListIterator<String> litr = recentlyUsedList.listIterator();

        while (litr.hasNext() && notInList) {
            String s = litr.next();
            if (s.equalsIgnoreCase(item)) {
                // Remove this element. It will be added to the beginning of the list
                recentlyUsedList.remove(s);
                notInList = false;
            }
        }

        if (notInList) {
            if (recentlyUsedList.size() == maxSize) {
                // The list is at its max size. Remove the oldest one.
                recentlyUsedList.remove(maxSize - 1);
            }
        }
        // Add the item to the beginning of the list.
        recentlyUsedList.add(0, item);
    }

    public void addAll(final String[] items) {
        // Read in reverse order, since items get added to the beginning
        for (int i = items.length - 1; i >= 0; --i) {
            add(items[i]);
        }
    }

    /**
     * This method gets the items from recentlyUsedList and puts them in an array of Strings
     * 
     * @return java.lang.String[]
     * 
     */
    public String[] getItems() {
        String[] items = new String[recentlyUsedList.size()];

        int i = 0;
        for (String s : recentlyUsedList) {
            items[i++] = s;
        }

        return (items);
    }

    /**
     * This method returns the number of elements in recentlyUsedList
     * 
     * @return java.lang.int
     * 
     */
    public int getListSize() {
        return (recentlyUsedList.size());
    }

    /**
     * This method returns maxSize
     * 
     * @return java.lang.int
     * 
     */
    public int getMaxSize() {
        return (maxSize);
    }

    /**
     * This method gets maxSize from the properties file
     * 
     * @return void
     * 
     * @throws FileNotFoundException, IOException
     */
    private void getMaxSizeFromProperties() throws MRUException {
        try {
            FileInputStream fis = new FileInputStream(propertiesFile);
            //Properties properties = new Properties();
            properties.load(fis);
            String maxSizeStr = properties.getProperty(sectionName + "." + "MaxSize", Integer.toString(defaultSize));
            Integer i = new Integer(maxSizeStr);
            maxSize = i;
            fis.close();
        } catch (IOException e) {
            throw new MRUException(e);
        }
    }

    /**
     * This method returns propertiesFile
     * 
     * @return java.util.File
     * 
     */
    public File getPropertiesFile() {
        return propertiesFile;
    }

    /**
     * This method returns propertiesFile's name
     * 
     * @return java.lang.String
     * 
     */
    public String getPropertiesFileName() {
        return (propertiesFile != null ? propertiesFile.getName() : null);
    }

    /**
     * This method returns sectionName
     * 
     * @return java.lang.String
     * 
     */
    public String getSectionName() {
        return (sectionName);
    }

    /**
     * This method loads a most recently used list from a properties file into recentlyUsedList
     * 
     * @return void
     * 
     * @throws MRUException, FileNotFoundException, IOException
     */
    public void load() throws MRUException {
        try {
            if (propertiesFile == null) {
                throw new MRUException("propertiesFile object not initialized");
            }
            if (sectionName == null) {
                throw new MRUException("sectionName object is not initialized");
            }
            if (maxSize == 0) {
                throw new MRUException("maxSize is not initialized");
            }
            if (!propertiesFile.exists()) {
                throw new MRUException(propertiesFile.getName() + " does not exist");
            }
            if (propertiesFile.isDirectory()) {
                throw new MRUException(propertiesFile.getName() + " is a directory, not a file");
            }
            if (sectionName.length() == 0) {
                throw new MRUException("sectionName holds an empty String");
            }


            FileInputStream fis = null;
            try {
                fis = new FileInputStream(propertiesFile);
                //Properties properties = new Properties();
                properties.load(fis);
            } finally {
                fis.close();
            }

            boolean fileNameFound = true;
            for (int i = 1; ((fileNameFound) && (i <= maxSize)); ++i) {
                String fileName = properties.getProperty(sectionName + "." + i);
                if (fileName != null && fileName.length() > 0) {
                    recentlyUsedList.add(fileName);
                } else {
                    fileNameFound = false;
                }
            }

        } catch (IOException e) {
            throw new MRUException(e);
        }
    }

    /**
     * This method removes the string item from recentlyUsedList
     * 
     * @return java.lang.boolean
     * 
     */
    public boolean remove(final String item) {
        return (recentlyUsedList.remove(item));
    }

    /**
     * This method clears the contents of recentlyUsedList
     * 
     * @return void
     * 
     */
    public void removeAll() {
        recentlyUsedList.clear();
    }

    /**
     * This method saves the contents of recentlyUsedList into propertiesFile
     * 
     * @return void
     * 
     * @throws MRUException, FileNotFoundException, IOException
     */
    public void save() throws MRUException {
        try {
            if (propertiesFile == null) {
                throw new MRUException("propertiesFile object not initialized");
            }
            if (sectionName == null) {
                throw new MRUException("sectionName object is not initialized");
            }
            if (maxSize == 0) {
                throw new MRUException("maxSize is not initialized");
            }
            if (!propertiesFile.exists()) {
                throw new MRUException(propertiesFile.getName() + " does not exist");
            }
            if (propertiesFile.isDirectory()) {
                throw new MRUException(propertiesFile.getName() + " is a directory, not a file");
            }
            if (sectionName.length() == 0) {
                throw new MRUException("sectionName holds an empty String");
            }

            //Properties properties = new Properties();
            int i = 1;
            for (String item : recentlyUsedList) {
                properties.setProperty(sectionName + "." + i++, item);
            }

            while (i <= maxSize) {
                properties.setProperty(sectionName + "." + i++, "");
            }
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(propertiesFile);
                properties.store(fos, null);
            } finally {
                fos.close();
            }

            saveMaxSize();
        } catch (IOException e) {
            throw new MRUException(e);
        }
    }

    /**
     * This method saves the contents of recentlyUsedList into properties, a Properties object that is passed in
     * 
     * @return void
     * 
     * @throws MRUException
     */
    public void saveInProperties(final Properties properties) throws MRUException {
        if (sectionName == null) {
            throw new MRUException("sectionName object is not initialized");
        }
        if (maxSize == 0) {
            throw new MRUException("maxSize is not initialized");
        }
        if (sectionName.length() == 0) {
            throw new MRUException("sectionName holds an empty String");
        }

        //Properties properties = new Properties();
        int i = 1;
        for (String item : recentlyUsedList) {
            properties.setProperty(sectionName + "." + i++, item);
        }

        while (i <= maxSize) {
            properties.setProperty(sectionName + "." + i++, "");
        }

        saveMaxSizeInProperties(properties);
    }

    /**
     * This method saves maxSize into propertiesFile
     * 
     * @return void
     * 
     * @throws MRUException, FileNotFoundException, IOException
     */
    public void saveMaxSize() throws MRUException {
        if (propertiesFile == null) {
            throw new MRUException("propertiesFile object not initialized");
        }
        if (sectionName == null) {
            throw new MRUException("sectionName object is not initialized");
        }
        if (maxSize == 0) {
            throw new MRUException("maxSize is not initialized");
        }
        if (!propertiesFile.exists()) {
            throw new MRUException(propertiesFile.getName() + " does not exist");
        }
        if (propertiesFile.isDirectory()) {
            throw new MRUException(propertiesFile.getName() + " is a directory, not a file");
        }
        if (sectionName.length() == 0) {
            throw new MRUException("sectionName holds an empty String");
        }

        //Properties properties = new Properties();
        properties.setProperty(sectionName + "." + "MaxSize", new Integer(maxSize).toString());

        FileOutputStream fos = null;
        try {
            try {
                fos = new FileOutputStream(propertiesFile);
                properties.store(fos, null);
            } finally {
                fos.close();
            }
        } catch (IOException e) {
            throw new MRUException(e);
        }
    }

    /**
     * This method saves maxSize into properties, a Properties object that is passed in
     * 
     * @return void
     * 
     * @throws MRUException
     */
    public void saveMaxSizeInProperties(final Properties properties) throws MRUException {
        if (sectionName == null) {
            throw new MRUException("sectionName object is not initialized");
        }
        if (maxSize == 0) {
            throw new MRUException("maxSize is not initialized");
        }
        if (sectionName.length() == 0) {
            throw new MRUException("sectionName holds an empty String");
        }

        properties.setProperty(sectionName + "." + "MaxSize", new Integer(maxSize).toString());
    }

    /**
     * This method initializes maxSize
     * 
     * @return void
     */
    public void setMaxSize(final int maxSize) throws MRUException {
        this.maxSize = maxSize;

        // If recentlyUsedList has more items than the new maxSize, remove the extras.
        if (recentlyUsedList.size() > maxSize) {
            ListIterator<String> litr = recentlyUsedList.listIterator(maxSize - 1);
            while (litr.hasNext()) {
                String s = litr.next();
                recentlyUsedList.remove(s);
            }
        }
    }

    /**
     * This method initializes propertiesFile
     * 
     * @return void
     */
    public void setPropertiesFile(final File propertiesFile) {
        this.propertiesFile = propertiesFile;
    }

    /**
     * This method initializes sectionName
     * 
     * @return void
     */
    public void setSectionName(final String sectionName) {
        this.sectionName = sectionName;
    }
}
